昨天我們實作了台灣電子發票規格逆向工程、發票特化局部放大與多重黑白二值化圖像增強演算法,在瀏覽器本地端達成了零秒、零延遲、免金鑰且不花一毛模型費用的強大辨識力。然而,當我們將發票掃描系統推廣給跨平台的真實使用者時,一組棘手的跨裝置體驗斷層隨之浮現:使用者在手機上拿著長發票拍照時,網頁內嵌視訊鏡頭常常對焦緩慢甚至無法微距對焦;而桌機或筆電使用者在面對細小收據字體時,卻苦於沒有光學變焦能力;更嚴重的是,現代手機隨手拍出的四千八百萬畫素超高解析度照片,若直接送入前端畫布運算,會瞬間觸發記憶體溢出導致行動瀏覽器直接閃退崩潰。
為了在不同終端設備上都能獲得穩定且清晰的成像品質,今天我們實作多端差異化相機調用架構、光學與數位雙軌變焦引擎,以及具備硬性上限的畫布動態採樣防爆演算法。手機端無縫喚起系統原生相機進行超高解析度微距對焦與圖庫批次多選,桌機端則透過 Web 視訊串流結合手動階梯變焦進行精確中央裁切;同時在前端建構畫布尺寸動態採樣管線,在確保發票細小文字清晰可辨的前提下,徹底根除手機記憶體崩潰的風險。
線上體驗 Live Demo:https://410355-collab.github.io/Premium-Business-Calculator/
(強烈建議使用 iPhone Safari 或 Android 手機開啟體驗!)
GitHub 開源倉庫:https://github.com/410355-collab/Premium-Business-Calculator
歡迎提出建議與指導
(開發環境:Google Antigravity IDE + Google Gemini 協同開發)
在跨平台網頁應用中處理高解析度影像與相機存取時,開發者普遍面臨三大技術瓶頸:
第一個問題是手機瀏覽器視訊串流對焦能力低落。許多網頁在手機端一律調用網頁串流介面,但在智慧型手機的瀏覽器沙盒限制下,網頁端往往無法直接操控手機多鏡頭模組的微距對焦或自動曝光鎖定。當使用者試圖拍攝一張細長的便利商店收據時,手機鏡頭只要稍微靠近紙張,畫面立刻變得模糊一片,導致後續演算法根本無法看清微小的品名與金額數字。
第二個問題是桌上型設備缺乏實體變焦與對齊能力。筆記型電腦或桌機的外接視訊鏡頭通常具備較大的廣角視野且焦距固定。當使用者手持一張小型發票湊到螢幕前時,收據在整個視訊畫面中僅佔據微小比例,且手持極易晃動。若軟體不提供放大與區域鎖定功能,使用者很難精準將單據置於掃描區域中央,嚴重影響辨識成功率。
第三個問題是現代超高畫質相片帶來的記憶體防爆危機。當今智慧型手機的主相機動輒具備四千八百萬至一億畫素。當使用者將一張高達八千乘以六千像素的原始照片載入網頁畫布時,未經壓縮的未經處理點陣圖會直接佔用數百百萬位元組的記憶體空間。在記憶體管理極其嚴苛的行動端瀏覽器中,這往往會立即觸發記憶體警戒線,導致整個網頁被作業系統無情強制重整或閃退。
為了解決成像品質與記憶體穩定的衝突,我們建立了因地制宜的跨裝置影像擷取管線:
手機端系統級相機喚起與圖庫多選:
我們在手機端介面上巧妙運用了環境鏡頭捕獲屬性。當手機使用者點擊拍照時,系統跳過受限的網頁內嵌串流,直接喚醒作業系統原生的高階相機應用程式。使用者可以從容利用手機原生的雷射對焦、夜景模式與多鏡頭切換拍出最高品質的清晰發票;同時支援在相簿中一次勾選多張發票批量上傳,徹底釋放行動端的原生攝影優勢。
筆電端光學與數位雙軌變焦快照:
針對桌機與筆電環境,系統提供內嵌的視訊串流視窗,並設計了一倍、一點五倍、二倍與三倍的階梯式變焦控制列。底層首先嘗試調用原生媒體軌道的高階約束介面,向鏡頭硬體申請光學變焦;若硬體不支援,系統即刻平滑回退至數位變焦模式,在視訊元素上套用三維縮放變形。當使用者按下拍攝快照時,畫布演算法會依據當前的變焦倍率,精準從視訊中央反向裁切出目標影像,宛如具備真實的微距對焦鏡頭。
畫布動態尺寸採樣與四千像素硬性防爆:
在影像進入辨識前的前端壓縮管線中,我們設計了三檔品質動態採樣機制(一千零二十四、一千六百與二千四百像素)。演算法會依據原圖長寬比例動態縮放尺寸;更關鍵的是,程式碼中強制設立了一道四千像素的硬性安全防禦天花板。無論使用者上傳多麼龐大的原始檔案,畫布邊界絕對嚴格受控於安全範圍內,既保證了微小文字具備足夠的辨識解析度,又從根本上杜絕了行動端記憶體崩潰的風險。
以下為雙軌動態變焦控制、中央裁切快照與具備記憶體防護之畫布壓縮函式的關鍵實作:
// 動態鏡頭變焦:相機硬體 Zoom 與 CSS 數位無縫備援
async function applyWebcamZoom(zoomFactor) {
const factor = parseFloat(zoomFactor) || 1;
const videoEl = document.getElementById('ai-webcam-video');
// 1. 嘗試調用原生相機硬體 Zoom API (支援外接高階鏡頭與硬件變焦)
if (webcamStream) {
const videoTrack = webcamStream.getVideoTracks()[0];
if (videoTrack && typeof videoTrack.getCapabilities === 'function') {
const capabilities = videoTrack.getCapabilities();
if (capabilities.zoom) {
try {
const minZoom = capabilities.zoom.min || 1;
const maxZoom = capabilities.zoom.max || 10;
const targetZoom = Math.min(Math.max(factor, minZoom), maxZoom);
await videoTrack.applyConstraints({ advanced: [{ zoom: targetZoom }] });
if (videoEl) videoEl.style.transform = 'scale(1)'; // 硬體變焦成功,重置樣式
currentCssZoom = 1;
return;
} catch (e) {
console.warn('Hardware zoom failed, fallbacking to CSS zoom:', e);
}
}
}
}
// 2. 備援方案:CSS 數位放大 (百分百相容筆電與所有瀏覽器)
if (videoEl) {
videoEl.style.transform = `scale(${factor})`;
currentCssZoom = factor;
}
}
// 從即時視訊串流拍攝快照(依變焦倍率精準裁切中央區域)
function captureWebcamSnapshot() {
const videoEl = document.getElementById('ai-webcam-video');
if (!videoEl || !videoEl.videoWidth) return;
const canvas = document.createElement('canvas');
const vW = videoEl.videoWidth;
const vH = videoEl.videoHeight;
canvas.width = vW;
canvas.height = vH;
const ctx = canvas.getContext('2d');
// 若啟用了數位變焦,依縮放倍數精確裁切中央區域,達成真變焦成像
if (currentCssZoom > 1) {
const cropW = vW / currentCssZoom;
const cropH = vH / currentCssZoom;
const cropX = (vW - cropW) / 2;
const cropY = (vH - cropH) / 2;
ctx.drawImage(videoEl, cropX, cropY, cropW, cropH, 0, 0, vW, vH);
} else {
ctx.drawImage(videoEl, 0, 0, vW, vH);
}
const dataUrl = canvas.toDataURL('image/jpeg', 0.85);
stopWebcam();
addUploadedImage(dataUrl);
}
// 前端 Canvas 圖片壓縮與 4000px 硬性記憶體防護演算法
function compressImage(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
let w = img.width;
let h = img.height;
const { maxDim, quality } = getQualityParams();
// 行動端記憶體防護核心:設立 4000px 硬性天花板,防止記憶體溢出
const HARD_MAX_DIM = 4000;
const effectiveMaxDim = Math.min(maxDim, HARD_MAX_DIM);
// 等比等寬縮放計算
if (w > effectiveMaxDim || h > effectiveMaxDim) {
if (w > h) {
h = Math.round((h * effectiveMaxDim) / w);
w = effectiveMaxDim;
} else {
w = Math.round((w * effectiveMaxDim) / h);
h = effectiveMaxDim;
}
}
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, w, h);
const dataUrl = canvas.toDataURL('image/jpeg', quality);
resolve(dataUrl);
};
img.onerror = reject;
img.src = e.target.result;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
在整合多端相機調用與影像壓縮的實踐中,我們針對細部體驗進行了深入調校:
隱藏原生檔案標籤與無縫點擊穿透:
在使用者介面上,瀏覽器原生的檔案上傳按鈕往往外觀粗糙且無法配合整體液態玻璃風格。我們將真實的原生輸入元素在層級結構中完全隱藏,並透過語意化標籤與程式化觸發邏輯,將使用者對高質感按鈕的點擊精準轉發至隱藏的原生相機與檔案選擇器,在維持極致介面美感的前提下,保有原生的相機拉起能力。
動態縮圖畫廊與即時狀態清理:
當使用者連續拍攝多張單據或在手機相簿中多選匯入時,介面會即時建構畫廊縮圖清單。為了避免資料殘留造成混淆,只要使用者點擊刪除某張照片,系統不僅立即重排縮圖佇列,更會同步抹除先前的辨識金額與合計標籤,確保輸入端影像與輸出端帳目始終處於絕對同步的狀態。
品質採樣設定本機持久化:
在低階手機或網路頻寬有限的漫遊環境下,使用者可能更偏好極速的低品質辨識;而在高畫質長發票辨識時,則需要較高的採樣解析度。我們將三檔品質選擇持久化儲存於瀏覽器設定中,使用者隨手調整的解析度參數會跨階段永久生效,兼顧了不同網路條件下的靈活性。
今天我們成功實作了手機原生相機直拍喚起、桌機光學與數位雙軌變焦快照,以及具備四千像素硬性防禦的前端畫布防爆演算法。徹底消除了跨裝置影像擷取的體驗斷層,讓手機端能充分利用系統級鏡頭拍出極致清晰的發票細節,桌機端能從容縮放對準收據,並為後續的大模型推論築起了一道牢不可破的記憶體安全防線。
然而,當我們帶著這套系統走出國門,在日本、韓國或泰國旅遊採購時,新的考驗隨之而來:當地的熱感應收據往往經過折疊與口袋摩擦,上方的日幣、韓元符號可能早已磨損模糊,甚至整張發票只印著一連串當地的冷門地名與門市代號。在這種情況下,AI 該如何知道這筆錢到底該換算成哪一種外幣?明天我們將揭開全自動地理感知黑科技——GPS 經緯度反查各國貨幣幾何演算法!
出國在便利商店買東西,拿到皺巴巴的收據連貨幣符號都磨損看不清,AI 該如何神準猜中這到底該算日圓、韓元還是歐元?明天我們將揭開原生地理幾何反查演算法,以及如何無痕將經緯度資訊注入 AI 語意模型的強大實踐,敬請期待!